package com.cold.demo.web.config.handlemapping;

import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;

import java.lang.reflect.Method;
import java.util.*;

/**
 * @author: zyc
 */
@Component
public class RequestMethodProcessor implements ApplicationContextAware {

    private ApplicationContext               applicationContext;
    private Map<String, List<BeanAndMethod>> methodMap = new HashMap<>();

    @Override
    public void setApplicationContext(ApplicationContext ctx) throws BeansException {
        this.applicationContext = ctx;
        initMethodMap(ctx);
    }

    private void initMethodMap(ApplicationContext ctx) {
        Map<String, Object> controller     = ctx.getBeansWithAnnotation(Controller.class);
        Map<String, Object> restController = ctx.getBeansWithAnnotation(RestController.class);
        Map<String, Object> con            = new HashMap<>();
        con.putAll(controller);
        con.putAll(restController);
        // url --->  bean + method
        con.forEach((beanName, bean) -> {
            System.out.println(beanName);
            Method[] declaredMethods = bean.getClass().getDeclaredMethods();// 不包含父类的
            for (Method method : declaredMethods) {
                if (method.getAnnotation(Primary.class) == null) {
                    continue;
                }

                DeleteMapping deleteMapping = method.getAnnotation(DeleteMapping.class);
                if (deleteMapping != null) {
                    add(bean, method, deleteMapping.value());
                }
                PutMapping putMapping = method.getAnnotation(PutMapping.class);
                if (putMapping != null) {
                    add(bean, method, putMapping.value());
                }
                PostMapping postMapping = method.getAnnotation(PostMapping.class);
                if (postMapping != null) {
                    add(bean, method, postMapping.value());
                }
                GetMapping getMapping = method.getAnnotation(GetMapping.class);
                if (getMapping != null) {
                    add(bean, method, getMapping.value());
                }
                RequestMapping requestMapping = method.getAnnotation(RequestMapping.class);
                if (requestMapping != null) {
                    add(bean, method, requestMapping.value());
                }
            }
        });
        this.methodMap.forEach((k, v) -> {
            v.sort(Comparator.comparingInt(BeanAndMethod::getSort));
        });
        System.out.println("methodMap初始化完成...");
        this.methodMap = Collections.unmodifiableMap(this.methodMap);
    }

    private void add(Object bean, Method method, String[] originalRequestUrl) {
        Map<String, List<BeanAndMethod>> methodMap = this.methodMap;

        Primary primary    = method.getAnnotation(Primary.class);
        String  replaceUrl = primary.replaceUrl();
        int     sortInt    = primary.value();

        BeanAndMethod beanAndMethod = new BeanAndMethod(bean, method, originalRequestUrl, sortInt);
        if (methodMap.containsKey(replaceUrl)) {
            methodMap.get(replaceUrl).add(beanAndMethod);
        } else {
            List<BeanAndMethod> list = new ArrayList<>();
            list.add(beanAndMethod);
            methodMap.put(replaceUrl, list);
        }
    }

    public Map<String, List<BeanAndMethod>> getMethodMap() {
        return methodMap;
    }

}
